﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;

namespace PushpinClusterer
{
    public static class TypeExtensions
    {
        public static MethodInfo GetMethodWp7Workaround(this Type type, string name)
        {
            if (name == null)
            {
                throw new ArgumentNullException("name");
            }

            return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, null, null);
        }

        private static MethodInfo GetMethod(
        Type type,
        string name,
        BindingFlags bindingFlags,
        Binder binder,
        CallingConventions callConvention,
        Type[] types,
        ParameterModifier[] modifiers)
        {
            if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly)
            {
                return types == null
                       ? type.GetMethod(name, bindingFlags)
                       : type.GetMethod(name, bindingFlags, binder, callConvention, types, modifiers);
            }

            bool isBaseType = false;
            bindingFlags = bindingFlags | BindingFlags.DeclaredOnly;
            MethodInfo result = null;
            while (result == null && type != null)
            {
                result =
                    types == null
                       ? type.GetMethod(name, bindingFlags)
                       : type.GetMethod(name, bindingFlags, binder, callConvention, types, modifiers);
                if (isBaseType && result != null && result.IsPrivate)
                {
                    result = null;
                }

                type = type.BaseType;
                if (!isBaseType)
                {
                    isBaseType = true;
                    bindingFlags = bindingFlags & (~BindingFlags.Static);
                }
            }

            return result;
        }

        private static bool HasAlreadyBeenFound(MethodInfo method, IEnumerable<MethodInfo> processedMethods)
        {
            if (!method.IsGenericMethodDefinition)
            {
                return processedMethods.Any(m => m.GetBaseDefinition().Equals(method.GetBaseDefinition()));
            }

            return processedMethods.Any(
                m => m.Name == method.Name &&
                     HaveSameGenericArguments(m, method) &&
                     HaveSameParameters(m, method));
        }

        private static bool HaveSameParameters(MethodInfo method1, MethodInfo method2)
        {
            var parameters1 = method1.GetParameters();
            var parameters2 = method2.GetParameters();
            return parameters1.Length == parameters2.Length &&
                   parameters1.All(parameters2.Contains);
        }

        private static bool HaveSameGenericArguments(MethodInfo method1, MethodInfo method2)
        {
            var genericArguments1 = method1.GetGenericArguments();
            var genericArguments2 = method2.GetGenericArguments();
            return genericArguments1.Length == genericArguments2.Length;
        }

    }
}
